package top.monddia.netty.client.client.handler;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.EventLoop;
import io.netty.handler.timeout.IdleStateEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import top.monddia.netty.client.client.NettyClient;
import top.monddia.netty.client.config.NettyConfig;
import top.monddia.netty.common.model.dto.MessageDto;
import top.monddia.netty.common.model.enums.MessageTypeEnums;
import top.monddia.netty.common.utils.IdUtils;

import java.util.concurrent.TimeUnit;

/**
 * Netty客户端消息处理器
 *
 * @author ZYP
 */
@Component
public class HeartHandler extends ChannelInboundHandlerAdapter {

    protected Logger logger = LoggerFactory.getLogger(getClass());

    private NettyConfig nettyConfig;

    /**
     * 客户端类
     */
    private NettyClient nettyClient;

    /**
     * 序列号工具类
     */
    private IdUtils idUtils;

    /**
     * 发送心跳时间
     */
    private long heartTime = 0L;

    /**
     * 客户端连续N次收不到服务端的PONG消息
     */
    private int unPongTimes = 0;

    public HeartHandler(NettyConfig nettyConfig, NettyClient nettyClient) {
        this.nettyConfig = nettyConfig;
        this.nettyClient = nettyClient;
        this.idUtils = new IdUtils();
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext channelHandlerContext, Object event) throws Exception {
        logger.info("Receive event: {}", event.toString());
        // 连接空闲
        if (event instanceof IdleStateEvent) {
            // 判断失败次数，超出次数则断开连接
            if (this.unPongTimes < this.nettyConfig.getUnPongRetryTimes()) {
                // 发送心跳消息
                sendPing(channelHandlerContext);
                // 计数增加
                this.unPongTimes++;
            } else {
                channelHandlerContext.channel().close();
            }
        } else {
            super.userEventTriggered(channelHandlerContext, event);
        }
    }

    /**
     * 处理断开重连
     *
     * @param channelHandlerContext
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext channelHandlerContext) throws Exception {
        logger.error("检测到心跳服务断开，将在5秒后进行重连");
        final EventLoop eventLoop = channelHandlerContext.channel().eventLoop();
        eventLoop.schedule(() -> this.nettyClient.connect(new Bootstrap(), eventLoop), 5L, TimeUnit.SECONDS);
        super.channelInactive(channelHandlerContext);
    }

    @Override
    public void channelRead(ChannelHandlerContext channelHandlerContext, Object msg) throws Exception {
        // 对象类型正确则处理
        if (msg instanceof MessageDto) {
            MessageDto messageDto = (MessageDto) msg;
            // 处理PONG消息
            if (messageDto.getMsgType() == MessageTypeEnums.PONG.getValue()) {
                // 更新变量
                this.unPongTimes = 0;
                // 延迟
                long delay = (System.currentTimeMillis() - this.heartTime) / 2;
                // TODO 监控信息
                logger.info("客户端与服务端的PING时间为" + delay + "毫秒");
            }
        }
        super.channelRead(channelHandlerContext, msg);
    }

    /**
     * 发送心跳消息
     *
     * @param channelHandlerContext
     */
    private void sendPing(ChannelHandlerContext channelHandlerContext) {
        // 更新心跳时间
        this.heartTime = System.currentTimeMillis();
        // 封装消息体
        MessageDto messageDto = new MessageDto();
        messageDto.setVersion(this.nettyClient.VERSION);
        messageDto.setMsgType(MessageTypeEnums.PING.getValue());
        messageDto.setSeq(this.idUtils.nextId());
        messageDto.setBody(new byte[0]);
        // 发送消息
        channelHandlerContext.writeAndFlush(messageDto);
    }
}
